<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root">
        <div>
            <div>{{name}}</div>
            <div>{{age}}</div>
            <div>{{gender}}</div>
            <ul>
                <li>1</li>
                <li>2</li>
                <li>3</li>
            </ul>
        </div>
    </div>

    <script>

        // 生成单个虚拟dom
        class Vnode{
            constructor(tag, data, value, type) {
                this.tag = tag && tag.toLowerCase();
                this.data = data;
                this.value = value;
                this.type = type;
                this.children = [];
            }

            appendChild(vnode) {
                this.children.push(vnode);
            }
        }

         // 生成虚拟dom
         function getVnode(node) {
            const nodeType = node.nodeType;
            let _vNode = null;
            if (nodeType === 1) {
                const tagName = node.nodeName;
                let data = {};
                const attrs = node.attributes;
                for (let index = 0; index < attrs.length; index++) {
                    data[attrs[index].nodeName] = attrs[index].nodeValue;
                }
                _vNode = new Vnode(tagName, data, undefined, nodeType);
                let childNodes = node.childNodes;
                for (let i = 0; i < childNodes.length; i++) {
                    _vNode.appendChild(getVnode(childNodes[i]));
                }
            } else {
                _vNode = new Vnode(undefined, undefined, node.nodeValue, nodeType);
            }
            return _vNode;
        }
        // 替换插值表达式
        function getValueBypath(obj, path) {
            const paths = path.split('.');
            let prop;
            let value = obj;
            while (prop = paths.shift()) {
                console.log('prop', prop);
                value = value[prop];
            }
            return value;
        }

        /*将带有插值的vnode 与数据结合生成 真实数据的vnode 
            模拟ast => vnode
        */
        function combine(vnode, data) {
            let _type = vnode.type;
            let _data = vnode.data;
            let _tag = vnode.tag;
            let _children = vnode.children;
            let _value = vnode.value;

            let _vnode = null;

            if ( _type === 1 ) {
                _vnode = new Vnode(_tag, _data, _value, _type);
                _children.forEach(subnode => {
                    _vnode.appendChild(combine(subnode, data));
                });
            } else if ( _type === 3) {
                // 对文本处理
                _value = _value.replace(/\{\{(.+?)\}\}/g, (_, g) => {
                    console.log(data);
                    return getValueBypath(data, g.trim());
                })
                _vnode = new Vnode(_tag, _data, _value, _type)
            }

            return _vnode;

        }
        // 将虚拟dom转换为真实dom
        function parseVnode(vNode) {
            let tag = vNode.tag;
            let type = vNode.type;
            let attrs = vNode.data;
            let children = vNode.children;
            let value = vNode.value;
            let _realDom = null;
            if (type === 1) {
                let el = document.createElement(tag);
                let props = Object.keys(attrs);
                let prop;
                while (prop = props.shift()) {
                    el.setAttribute(prop, attrs[prop]);
                }
                if (children.length) {
                    for (let i = 0; i < children.length; i++) {
                        el.appendChild(parseVnode(children[i]));
                    }
                }
                _realDom = el;
            } else {
                let textNode = document.createTextNode(value);
                _realDom = textNode;
            }
            return _realDom;
        }

        function JGvue(options) {
            this._data = options.data;
            this._template = document.querySelector(options.el);
            this._parent = this._template.parentNode;
            this.mount();
        }

        JGvue.prototype.mount = function() {
            this.render = this.createRenderFn()
            this.mountComponent()
            // render 方法 生成虚拟dom
        }

        JGvue.prototype.mountComponent = function() {
            
            // 执行mountComponent
            let mount = () => {
                this.update(this.render())
            };

            mount.call(this)
        }

        JGvue.prototype.createRenderFn = function() {

            // 缓存 
            let ast = getVnode(this._template)

        // render 方法 生成虚拟dom
            return function render(params) {
                let _temp = combine(ast, this._data);
                debugger
                return _temp;
            }
        }
        // 将虚拟dom渲染到页面中 里面应用到diff算法
        JGvue.prototype.update = function (vnode) {
            // 简化  直接生成dom  replaceChild到页面中

            let realDom = parseVnode(vnode);
            this._parent.replaceChild(realDom, this._template)

        }


        let app = new JGvue({
            el: '#root',
            data: {
                name: 'songtianrui',
                gender: '男',
                age: 18
            }
        })
    </script>
</body>
</html>